#pragma once

#include "log.hpp"
#include "protocol.hpp"
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <functional>

#define gbacklog 5

using namespace std;

enum
{
    USAGE_ERR = 1,
    SOCK_ERR,
    BIND_ERR,
    LISTEN_ERR,
    READ_ERR
};

typedef function<void(const Request &, Response &)> func_t;

void handlerEnter(int sock, func_t callback)
{
    string inbuf; // 服务端接收缓冲区
    while (1)
    {
        // 1. 读取。
        // 1.1 如何保证读取到的消息是 【一个】【完整的】请求
        string req_str, text;
        if (!recvPackage(sock, inbuf, req_str)) // 接收一个完整报文
            return;

        std::cout << "带报头:\n"
                  << req_str << endl;

        if (!deLength(req_str, text)) // 提取正文
            return;                   //"10 + 10"

        std::cout << "去掉报头，正文:\n"
                  << text << endl;

        // 2. 对读取到的请求Request反序列化
        //  即得到Request对象
        Request req;
        req.deserialize(&text); // 反序列化把成员从text中读出

        // 3. 计算机处理，业务逻辑。
        // 得到响应Response
        Response resp;
        callback(req, resp); // 计算，结果放入Response对象

        // 4. 对响应Response序列化
        string resp_str;
        resp.serialize(&resp_str); // 0 20

        cout << "序列化的结果:\n"
             << resp_str << endl;

        string send_str;
        if (!enLength(resp_str, send_str))
            return; //"4\r\n0 20\r\n"

        cout << "最终报文的结果:\n"
             << send_str << endl;

        // 5. 发送
        send(sock, send_str.c_str(), send_str.size(), 0);
    }
}

class Server
{
public:
    Server(const uint16_t &port, func_t func)
        : _listenSock(-1), _port(port), _func(func)
    {
    }

    void initServer()
    {
        // 1.创建套接字
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0)
        {
            logMessage(FATAL, "监听套接字创建失败");
            exit(SOCK_ERR);
        }
        logMessage(NORMAL, "创建套接字成功: %d", _listenSock);

        // 2.绑定端口号
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        int n = bind(_listenSock, (struct sockaddr *)&local, sizeof(local));
        if (n == -1)
        {
            logMessage(FATAL, "绑定端口号失败");
            exit(BIND_ERR);
        }
        logMessage(NORMAL, "绑定端口号成功");

        // 3.设置监听状态
        n = listen(_listenSock, gbacklog);
        if (n < 0)
        {
            logMessage(FATAL, "监听设置失败");
            exit(LISTEN_ERR);
        }
        logMessage(NORMAL, "监听设置成功");
    }

    void start()
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        while (1)
        {
            signal(SIGCHLD, SIG_IGN); // version 2

            // 4.接收连接请求，建立连接
            int sock = accept(_listenSock, (struct sockaddr *)&client, &len);
            if (sock < 0)
            {
                logMessage(ERROR, "接收失败"); // 接首失败 不退出，继续重新接收
                continue;
            }
            logMessage(NORMAL, "链接成功, 新套接字：%d", sock);

            /********************version2 多进程，子进程会拷贝父进程的进程地址空间*****************/
            pid_t id = fork();
            if (id == 0) // 子进程
            {
                close(_listenSock);

                handlerEnter(sock, _func);
                close(sock);
                exit(0);
            }

            close(sock);
        }
    }

    ~Server()
    {
    }

private:
    int _listenSock;
    uint16_t _port;
    func_t _func;
};
